package cn.bass.marketing.service.assembly;

import cn.bass.marketing.common.constant.RulesConstant;
import cn.bass.marketing.common.exception.AppException;
import cn.bass.marketing.common.vo.resp.MarketingStrategyPrizeRespVO;
import cn.bass.marketing.common.vo.resp.MarketingStrategyRespVO;
import cn.bass.marketing.common.vo.resp.MarketingStrategyRuleRespVO;
import cn.bass.marketing.redis.core.IRedisService;
import cn.bass.marketing.service.strategy.MarketingStrategyService;
import cn.bass.marketing.service.strategyPrize.MarketingStrategyPrizeService;
import cn.bass.marketing.service.strategyRule.MarketingStrategyRuleService;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

import static cn.bass.marketing.common.exception.enums.GlobalErrorCodeConstants.STRATEGY_PRIZE_NOT_FOUND;

/**
 * {@author}: LiuWei
 * {@version}: 1.0
 * {@since}: 2017-11-23 10:49
 * {@link}: <a href="https://github.com/LiuWei">...</a>
 * 描述：营销策略装配实现类
 */

@Slf4j
@Service
public class MarketingStrategyAssemblyImpl implements MarketingStrategyAssembly, MarketingStrategyAssemblyDispatch {


    @Resource
    private MarketingStrategyPrizeService marketingStrategyPrizeService;
    @Resource
    private IRedisService redisService;
    @Resource
    private MarketingStrategyService marketingStrategyService;
    @Resource
    private MarketingStrategyRuleService marketingStrategyRuleService;

    @Override
    public boolean assemblyLotteryStrategy(String strategyId) {
        // 查询策略奖品信息
        List<MarketingStrategyPrizeRespVO> marketingStrategyPrizeDos = marketingStrategyPrizeService.selectEnableStrategyPrizeList(strategyId);
        if (marketingStrategyPrizeDos == null || CollUtil.isEmpty(marketingStrategyPrizeDos)) {
            throw new AppException(STRATEGY_PRIZE_NOT_FOUND.getCode(), STRATEGY_PRIZE_NOT_FOUND.getMsg());
        }
        assemblyLotteryStrategy(strategyId, marketingStrategyPrizeDos);

        // 权重策略 权重规则配置
        MarketingStrategyRespVO marketingStrategyRespVO = marketingStrategyService.selectById(strategyId);
        if (null == marketingStrategyRespVO) {
            return true;
        }
        // 匹配规则权重
        RulesConstant ruleBlacklistKey = RulesConstant.getRuleBlacklist(marketingStrategyRespVO.getCRuleModels());
        if (null == ruleBlacklistKey) {
            return true;
        }

        // 查询规则信息
        MarketingStrategyRuleRespVO marketingStrategyRuleRespVO = marketingStrategyRuleService.selectByStrategyIdAndRuleModel(strategyId, ruleBlacklistKey);
        if (null == marketingStrategyRuleRespVO) {
            throw new AppException(STRATEGY_PRIZE_NOT_FOUND.getCode(), STRATEGY_PRIZE_NOT_FOUND.getMsg());
        }

        Map<String, List<Integer>> ruleModelsMap = marketingStrategyRuleRespVO.getRuleModels();
        Set<String> keySet = ruleModelsMap.keySet();
        for (String key : keySet) {
            List<Integer> ruleModelList = ruleModelsMap.get(key);
            ArrayList<MarketingStrategyPrizeRespVO> marketingStrategyPrizeRespClone = Lists.newArrayList(marketingStrategyPrizeDos);
            marketingStrategyPrizeRespClone.removeIf(item -> !ruleModelList.contains(item.getCPrizeId()));
            assemblyLotteryStrategy(strategyId.concat("_").concat(key), marketingStrategyPrizeRespClone);
        }

        return true;
    }

    private void assemblyLotteryStrategy(String strategyId, List<MarketingStrategyPrizeRespVO> marketingStrategyPrizeDos) {
        // 查询最小概率奖品
        BigDecimal minRate = marketingStrategyPrizeDos.stream().map(MarketingStrategyPrizeRespVO::getCPrizeRate).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        // 计算概率的总和
        BigDecimal sumRate = marketingStrategyPrizeDos.stream().map(MarketingStrategyPrizeRespVO::getCPrizeRate).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 计算概率总和除以最小概率获取奖品范围
        BigDecimal rateRange = sumRate.divide(minRate, 0, RoundingMode.CEILING);

        // 计算奖品范围表
        List<Integer> strategyPrizeRangeTable = getPrizeRangeTable(marketingStrategyPrizeDos, minRate);

        // 打乱奖品范围表
        Collections.shuffle(strategyPrizeRangeTable);

        // 保存到Map中
        Map<Integer, Integer> strategyPrizeRangeMap = new HashMap<>();
        for (int i = 0; i < strategyPrizeRangeTable.size(); i++) {
            if (strategyPrizeRangeTable.get(i) == null) {
                continue;
            }
            strategyPrizeRangeMap.put(i, strategyPrizeRangeTable.get(i));
        }

        // 保存查找表到Redis中
        marketingStrategyPrizeService.saveCacheStrategyPrizeRangeMap(strategyId, strategyPrizeRangeMap.size(), strategyPrizeRangeMap);
    }


    @Override
    public Integer getRandomPrizeId(String strategyId) {
        Integer rateRange = marketingStrategyPrizeService.getCacheRateRange(strategyId);
        return marketingStrategyPrizeService.getCacheStrategyPrizeRangeMap(strategyId, new SecureRandom().nextInt(rateRange));
    }


    @Override
    public Integer getRandomPrizeId(String strategyId, String ruleWeightValue) {
        String cacheKey = strategyId.concat("_").concat(ruleWeightValue);
        Integer rateRange = marketingStrategyPrizeService.getCacheRateRange(cacheKey);
        return marketingStrategyPrizeService.getCacheStrategyPrizeRangeMap(cacheKey, new SecureRandom().nextInt(rateRange));
    }

    private static List<Integer> getPrizeRangeTable(List<MarketingStrategyPrizeRespVO> marketingStrategyPrizeDos, BigDecimal minRate) {
        List<Integer> strategyPrizeRangeTable = new ArrayList<>();
        for (MarketingStrategyPrizeRespVO marketingStrategyPrizeDo : marketingStrategyPrizeDos) {
            Integer cPrizeId = marketingStrategyPrizeDo.getCPrizeId();
            BigDecimal cPrizeRate = marketingStrategyPrizeDo.getCPrizeRate();
            // 计算出每个奖品的概率需要存放到查找表数量, 循环填充
            for (int i = 0; i < cPrizeRate.divide(minRate, 0, RoundingMode.CEILING).intValue(); i++) {
                strategyPrizeRangeTable.add(cPrizeId);
            }
        }
        return strategyPrizeRangeTable;
    }
}
